李守中

SSH 客户端

Table of Contents

1. 基本用法

ssh 是典型的 C/S 架构的软件,最常见的用途是登录服务器,这要求服务器上必须运行 SSH 的守护进程 sshd。

ssh 登录服务器的命令如下:

  1. ssh <host>: <host> 可以是域名,IP 地址或局域网内部的主机名。
  2. ssh <user_name>@<host>: 指定登入主机的用户的名称,使用 @ 分隔。在不指定用户名的情况下,将使用客户端当前的用户名称作为登录远程服务器的用户名。
  3. ssh -l <user_name> <host>: 另一种指定用户名的方式。

ssh 默认连接服务器 22 端口,使用 -p 参数可以指定其他端口,比如 ssh -p 8821 lishouzhong.com 连接服务器 lishouzhong.com8821 端口。

2. 连接流程

ssh 连接远程服务器后,启动验证流程:

  1. 如果首次连接该服务器 ( 找不到服务器指纹 ),确认是否连接。
  2. 如果该服务器有指纹留存,直接连接。

第一次连接服务器,命令行会显示一段文字:

The authenticity of host 'lishouzhong.com (10.12.161.193)' can't be established.
ECDSA key fingerprint is SHA256:C9Ix5mnGqC70au1W7H6AZX/PCqlMcfnyCOa6Z1ny7Rs.
Are you sure you want to continue connecting (yes/no)?

意思是 ssh 客户端没见过 lishouzhong.com 这台服务器的指纹,是否继续连接 ( 输入 yesno )。 服务器指纹 指 SSH 服务器公钥的哈希值。每台 SSH 服务器都唯一一对密钥用于跟客户端通信,其中公钥的哈希值可以用来识别服务器。

ssh-keygen -l -f /etc/ssh/ssh_host_ecdsa_key.pub 可以查看服务器采用 ecdsa 算法生成的公钥的指纹 ( 读取文件中的公钥,计算并输出其哈希值 ),其中:

  • -f 指定密钥文件的路径。
  • -l 显示指定公钥文件的指纹。对于 RSA 和 DSA 密钥,ssh-keygen 尝试查找匹配的公共密钥文件并打印其指纹。
  • 如果使用 -lv 两个选项,则指纹将随密钥一起提供可视化的 ASCII 图形表示。

ssh 会将本机连接过的所有服务器公钥的指纹,储存在本机 ~/.ssh/known_hosts 文件中。每次连接服务器时,ssh 客户端会把接收到的服务器指纹与 known_hosts 文件内已经记录的指纹进行比对,来判断服务器是否陌生 ( 陌生的公钥哈希值 )。

在提示是否继续链接之后,输入 yes 并回车,就可以将当前服务器的指纹也储存在本机 ~/.ssh/known_hosts 文件中,下次连接不会再出现警告。同时,添加指纹后有下面的提示:

Warning: Permanently added 'lishouzhong.com' (ECDSA) to the list of known hosts.

最后,客户端与服务器建立连接,ssh 要求用户输入所要登录账户的密码,完成登录。

3. 服务器密钥变更

SSH 公钥的指纹 ( 是对公钥做 sha256 得到的字符串 ) 可以用于识别主机,防止有人恶意冒充主机身份。如果服务器的密钥发生变更 ( 比如重装 SSH 服务 ),SSH 客户端再次发起连接时,就会检测到本地记录的服务器公钥指纹与本次连接收到的公钥指纹不匹配。这时,客户端就会中断连接,并显示一段警告信息:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the RSA host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
77:a5:69:81:9b:eb:40:76:7b:13:04:a9:6c:f4:9c:5d.
Please contact your system administrator.
Add correct host key in /home/<username>/.ssh/known_hosts to get rid of this message.
Offending key in /home/<username>/.ssh/known_hosts:36

意思是,本次获取到的公钥指纹跟 SSH 客户端所在主机的 ~/.ssh/known_hosts 文件中所记录的公钥指纹不同,必须处理以后才能连接。

这时,需要确认是什么原因,使得服务器公钥指纹发生变更。到底是恶意劫持,还是管理员变更了服务器上的 SSH 公钥。

如果新的公钥确认可以信任,需要继续执行连接,可以在 SSH 客户端所在机器上执行 ssh-keygen -R <host> 将原来的公钥指纹从 ~/.ssh/known_hosts 文件内删除。

除了使用上面的命令,也可以手动修改 SSH 客户端所在机器的 ~/.ssh/known_hosts 文件,删除旧的公钥指纹。

删除旧的公钥指纹以后,重新执行 ssh 命令连接远程服务器,将新的指纹加入 ~/.ssh/known_hosts 文件,就可以顺利连接了。

4. ssh 命令行配置项

ssh 命令有很多配置项,修改它的默认行为。

-c 指定加密算法,可选 blowfish3des: ssh [-c blowfish | -c 3des] <host>

-C 表示压缩数据传输: ssh -C <host>

-d 设置打印的 debug 信息级别,数值越高,输出的内容越详细: ssh –d 1 <host>

-D 动态端口转发,指定本机的 Socks 监听端口,该端口收到的请求,都将转发到远程 SSH 主机 ssh -D 1080 <server> 将本机 1080 端口收到的请求,都转发到 <server>

-L 本地端口转发: ssh -L [<local_host>:]<local_port>:<target_host>:<target_port> <tunnel_host> 所有发向本地主机 <local_host> 上的端口 <local_port> 的请求,都会转发到 SSH 跳板机 <tunnel_host> ,然后 SSH 跳板机作为中介,将收到的请求发到目标服务器 <target_host> 的目标端口 <target_port> 。详见 SSH 端口转发

-R 远程端口转发。假定有 A B C 三台主机,A 在外网,B C 在内网,B 是跳板机,A 不能访问 B 而 B 可以访问 A: ssh -R <port_a>:<ip_c>:<port_c> -N <user_a>@<ip_a> ( 命令需在跳板机执行 ) 让 A 监听自己的 <port_a> 端口,并且数据由 <port_a> 流到 B 以后再经由 B 将数据包转发到 C 的 <port_c> 端口。详见 SSH 端口转发

-f 表示 SSH 连接在后台运行。

-F 指定配置文件 ssh -F </path/to/ssh_config>

-i 用于指定私钥,意为 identity_file ,值为密钥文件路径,默认为 ~/.ssh/id_dsa 。要使用指定私钥,对应的公钥必须存放到服务器。详见 SSH 服务器

-l 指定远程主机上的用户名: ssh -l <user_name> <host> = ssh <user_name>@<host>

-m 指定校验数据完整性的算法,意为 message authentication code 简称 MAC 。数据校验算法可以是 hmac-sha1hmac-md5: ssh -m [hmac-sha1 | hmac-md5] <host>

-o 用来指定一个配置命令,格式为 ssh -o "<keyword> <value>" 或者 ssh -o "<keyword>=<value>" 。现配置文件里面有如下内容:

User sally
Port 220

命令的等效写法为 ssh -o "User sally" -o "Port 220" <host>ssh -o User=sally -o Port=220 <host> 。使用等号时,配置命令可以不用写在引号里面,但等号前后不能有空格。

-p 指定 SSH 客户端连接的服务器端口: ssh -p <port> <host>

-q 表示安静模式 quiet ,不向用户输出任何警告信息。

-t 在 ssh 直接运行远端命令时,提供一个互动式 Shell: ssh -t <host> emacs

-v 显示详细信息可以重复多次,表示信息的详细程度,比如 -vv-vvv: ssh -vv <host>, ssh -vvv <host>

-V 输出 ssh 客户端的版本: ssh –V

-X 表示打开 X 窗口转发: ssh -X <host>

-1 指定使用 SSH 1 协议: ssh -1 <host>

-2 指定使用 SSH 2 协议: ssh -2 <host>

-4 指定使用 IPv4 协议,默认值: ssh -4 <host>

-6 指定使用 IPv6 协议: ssh -6 <host>

5. 客户端配置文件

SSH 客户端的全局配置文件 /etc/ssh/ssh_config。

用户配置文件 ~/.ssh/config ,优先级高于全局配置文件。

除了配置文件,~/.ssh 目录还有一些用户个人的密钥文件和其他文件。下面是其中一些常见的文件:

  • ~/.ssh/id_ecdsa: 用户的 ECDSA 私钥。
  • ~/.ssh/id_ecdsa.pub: 用户的 ECDSA 公钥。
  • ~/.ssh/id_rsa: 用于 SSH 协议版本 2 的 RSA 私钥。
  • ~/.ssh/id_rsa.pub: 用于SSH 协议版本 2 的 RSA 公钥。
  • ~/.ssh/identity: 用于 SSH 协议版本 1 的 RSA 私钥。
  • ~/.ssh/identity.pub: 用于 SSH 协议版本 1 的 RSA 公钥。
  • ~/.ssh/known_hosts: 包含 SSH 服务器的公钥指纹。

用户配置文件 ~/.ssh/config,可以按照不同服务器,列出各自的连接参数,从而不必每一次登录都输入重复的参数。给一个例子:

Host *
    Port 2222

Host remoteserver
    HostName remote.example.com
    User neo
    Port 2112

Host * 表示对所有主机生效,后面的 Port 2222 表示所有主机的默认连接端口都是 2222,这样就不用在登录时特别指定端口了。这里的缩进并不是必需的,只是为了视觉上,易于识别针对不同主机的设置。

Host remote_server 表示,下面的设置只对主机 remote_server 生效。 remoteserver 只是一个别名,具体的主机由 HostName 指定, UserPort 这两项分别表示用户名和端口。这里的 Port 会覆盖上面 Host * 部分的 Port 设置。

以后,登录 remote.example.com 时,只要执行 ssh remoteserver 就会自动套用 ~/.ssh/config 文件里面指定的参数。

remoteserver 的配置等效于命令 ssh -p 2112 [email protected]

ssh 客户端配置文件的每一行,就是一个配置命令。配置命令与对应的值之间,可以使用空格,也可以使用等号。

# #开头的行表示注释,会被忽略。空行等同于注释。
Compression yes
# 等同于
Compression = yes

下面是 ssh 客户端的一些主要配置命令,以及它们的范例值:

  • AddressFamily inet 只使用 IPv4 协议。如果设为 inet6 ,表示只使用 IPv6 协议。
  • BindAddress <local_host_ip> 指定本机的 IP 地址 ( 如果本机有多个 IP 地址 )。
  • CheckHostIP yes 检查 SSH 服务器的 IP 地址是否跟公钥数据库吻合。
  • Ciphers blowfish,3des 指定加密算法。
  • Compression yes 是否压缩传输信号。
  • ConnectionAttempts 10 客户端进行连接时,最大的尝试次数 ( 10 次 )。
  • ConnectTimeout 60 客户端进行连接时,服务器在指定秒数 ( 60s ) 内没有回复,则中断连接尝试。
  • DynamicForward <port> 指定动态转发端口。
  • GlobalKnownHostsFile </path/to/global_hosts_file> 指定全局公钥数据库文件位置。
  • Host <host> 指定连接的域名或 IP 地址,也可以是别名,支持通配符。Host 命令后面的所有配置,都是针对该主机的,直到下一个 Host 命令为止。
  • HostKeyAlgorithms ssh-dss,ssh-rsa 指定密钥算法,优先级从高到低排列。
  • HostName <host_name> 在 Host 命令使用别名的情况下,HostName 指定域名或 IP 地址。
  • IdentityFile </path/to/key_file> 指定私钥文件。
  • LocalForward <target_port> <local_host>:<loal_port>* 本地端口转发。将发给 <local_host> 主机 <local_port> 端口的流量转发给同主机的 <target_port> 端口。
  • LogLevel QUIET 指定日志详细程度。设为 QUIET 将不输出大部分的警告和提示。
  • MACs hmac-sha1,hmac-md5 指定数据校验算法。
  • NumberOfPasswordPrompts 2: 密码登录时,用户输错密码的最大尝试次数 ( 2 次 )。
  • PasswordAuthentication no 指定是否支持密码登录。不过,这里只是客户端禁止,真正的禁止需要在 SSH 服务器设置。
  • Port <port> 指定客户端连接的 SSH 服务器端口。
  • PreferredAuthentications publickey,hostbased,password 指定各种登录方法的优先级。
  • Protocol 2[,1] 支持的 SSH 协议版本,多个版本之间使用逗号分隔。
  • PubKeyAuthentication [yes | no] 是否支持密钥登录。这里只是客户端设置,还需要在 SSH 服务器进行相应设置。
  • RemoteForward <local_port> <remote_host>:<remote_port> 指定远程端口转发。
  • SendEnv [EnvName1, EnvName2 ...] SSH 客户端向服务器发送的环境变量名,多个环境变量之间使用空格分隔。环境变量的值从客户端当前环境中拷贝。
  • ServerAliveCountMax <number>* 如果没有收到服务器的回应,客户端连续发送多少次 keepalive 信号,才断开连接。默认值为 3。
  • ServerAliveInterval 300 客户端建立连接后,如果在给定秒数内,没有收到服务器发来的消息,客户端向服务器发送 keepalive 消息。如果不希望客户端发送,这一项设为 0。
  • StrictHostKeyChecking yes 表示严格检查,服务器公钥为未知或发生变化,则拒绝连接。 no 表示如果服务器公钥未知,则加入客户端公钥数据库,如果公钥发生变化,不改变客户端公钥数据库,输出一条警告,依然允许连接继续进行。默认值 ask 表示询问用户是否继续进行。
  • TCPKeepAlive yes 客户端是否定期向服务器发送keepalive信息。
  • User <user_name> 指定远程登录的账户名。
  • UserKnownHostsFile </path/to/my_local_hosts_file> 指定当前用户的 known_hosts 文件 ( 服务器公钥指纹列表 ) 的位置。
  • VerifyHostKeyDNS yes 是否通过检查 SSH 服务器的 DNS 记录,确认公钥指纹是否与 known_hosts 文件保存的一致。

6. 执行远程命令

ssh <user_name>@<host> <command> 使得 SSH 在登录成功后,立刻在远程主机上执行命令 <command> 。比如 ssh [email protected] cat /etc/hosts 可以在登录成功后,立即在远程主机上执行命令 cat /etc/hosts

采用这种语法执行命令时,ssh 客户端不会提供互动式的 Shell 环境,而是直接远程命令的执行结果输出在命令行。

但是,有些命令需要互动式的 Shell 环境,这时就要使用-t参数。

# 报错
$ ssh remote.server.com emacs
emacs: standard input is not a tty

# 不报错
$ ssh -t server.example.com emacs

上面代码中,emacs 命令需要一个互动式 Shell,所以报错。只有加上 -t 参数,ssh 才会分配一个互动式 Shell。



Last Update: 2023-05-27 Sat 16:17

Generated by: Emacs 28.2 (Org mode 9.5.5)   Contact: [email protected]

若正文中无特殊说明,本站内容遵循: 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议